#include "StringValidator.h"

const std::regex& NahidaProject::StringValidator::GetRegex(ValidationType type) const {
	auto it = compiledRegex.find(type);
	if (it == compiledRegex.end()) {
		auto patternIt = regexPatterns.find(type);
		if (patternIt != regexPatterns.end()) {
			compiledRegex[type] = std::regex(patternIt->second);
			it = compiledRegex.find(type);
		}
		else {
			throw std::invalid_argument("Unknown validation type");
		}
	}
	return it->second;
}

NahidaProject::StringValidator::ValidationResult NahidaProject::StringValidator::ValidateRegex(const std::string& str, const std::string& pattern) const {
	try {
		std::regex re(pattern);
		return ValidationResult(std::regex_match(str, re), "Pattern mismatch");
	}
	catch (const std::regex_error& e) {
		return ValidationResult(false, "Invalid regex pattern: " + std::string(e.what()));
	}
}

NahidaProject::StringValidator::ValidationResult NahidaProject::StringValidator::Validate(const std::string& str, ValidationType type) const {
	if (str.empty()) {
		return ValidationResult(false, "String is empty");
	}

	try {
		const std::regex& re = GetRegex(type);
		bool match = std::regex_match(str, re);

		std::string typeName;
		switch (type) {
		case ValidationType::EMAIL: typeName = "email"; break;
		case ValidationType::PHONE: typeName = "phone"; break;
		case ValidationType::URL: typeName = "URL"; break;
		case ValidationType::IP_ADDRESS: typeName = "IP address"; break;
		case ValidationType::MAC_ADDRESS: typeName = "MAC address"; break;
		case ValidationType::DATE: typeName = "date"; break;
		case ValidationType::TIME: typeName = "time"; break;
		case ValidationType::DATETIME: typeName = "datetime"; break;
		case ValidationType::NUMBER: typeName = "number"; break;
		case ValidationType::INTEGER: typeName = "integer"; break;
		case ValidationType::FLOAT: typeName = "float"; break;
		case ValidationType::HEX: typeName = "hexadecimal"; break;
		case ValidationType::BASE64: typeName = "base64"; break;
		case ValidationType::UUID: typeName = "UUID"; break;
		case ValidationType::CREDIT_CARD: typeName = "credit card"; break;
		case ValidationType::POSTAL_CODE: typeName = "postal code"; break;
		default: typeName = "custom format"; break;
		}

		return ValidationResult(match, match ? "" : "Invalid " + typeName + " format");
	}
	catch (const std::exception& e) {
		return ValidationResult(false, "Validation error: " + std::string(e.what()));
	}
}

NahidaProject::StringValidator::ValidationResult NahidaProject::StringValidator::ValidateLength(const std::string& str, size_t minimumLength, size_t maximumLength) const {
	size_t length = str.length();
	if (length < minimumLength) {
		return ValidationResult(false, "String too short, minimum length is " + std::to_string(minimumLength));
	}
	if (length > maximumLength) {
		return ValidationResult(false, "String too long, maximum length is " + std::to_string(maximumLength));
	}
	return ValidationResult(true);
}

NahidaProject::StringValidator::ValidationResult NahidaProject::StringValidator::ValidateCharset(const std::string& str, const std::string& allowedChars) const {
	for (char c : str) {
		if (allowedChars.find(c) == std::string::npos) {
			return ValidationResult(false, "Invalid character: " + std::string(1, c));
		}
	}
	return ValidationResult(true);
}

NahidaProject::StringValidator::ValidationResult NahidaProject::StringValidator::ValidateCustom(const std::string& str, std::function<bool(const std::string&)> validator, const std::string& errorMessage) const {
	try {
		bool result = validator(str);
		return ValidationResult(result, result ? "" : errorMessage);
	}
	catch (const std::exception& e) {
		return ValidationResult(false, "Custom validation error: " + std::string(e.what()));
	}
}

NahidaProject::StringValidator::ValidationResult NahidaProject::StringValidator::ValidateMultiple(const std::string& str, const std::vector<std::pair<ValidationType, std::string>>& validations) const {
	for (const auto& validation : validations) {
		ValidationResult result = Validate(str, validation.first);
		if (!result.isValid) {
			return ValidationResult(false, validation.second.empty() ? result.errorMessage : validation.second);
		}
	}
	return ValidationResult(true);
}

void NahidaProject::StringValidator::AddCustomPattern(ValidationType type, const std::string& pattern) {
	regexPatterns[type] = pattern;
	compiledRegex.erase(type);
}

NahidaProject::StringValidator::ValidationResult NahidaProject::StringValidator::ValidateEmail(const std::string& email) const {
	return Validate(email, ValidationType::EMAIL);
}

NahidaProject::StringValidator::ValidationResult NahidaProject::StringValidator::ValidatePhone(const std::string& phone) const {
	return Validate(phone, ValidationType::PHONE);
}

NahidaProject::StringValidator::ValidationResult NahidaProject::StringValidator::ValidateURL(const std::string& url) const {
	return Validate(url, ValidationType::URL);
}

NahidaProject::StringValidator::ValidationResult NahidaProject::StringValidator::ValidateIPAddress(const std::string& ip) const {
	return Validate(ip, ValidationType::IP_ADDRESS);
}

NahidaProject::StringValidator::ValidationResult NahidaProject::StringValidator::ValidateDate(const std::string& date) const {
	return Validate(date, ValidationType::DATE);
}

NahidaProject::StringValidator::ValidationResult NahidaProject::StringValidator::ValidateNumber(const std::string& number) const {
	return Validate(number, ValidationType::NUMBER);
}

NahidaProject::StringValidator::ValidationResult NahidaProject::StringValidator::ValidateInteger(const std::string& integer) const {
	return Validate(integer, ValidationType::INTEGER);
}

NahidaProject::StringValidator::ValidationResult NahidaProject::StringValidator::ValidateHex(const std::string& hex) const {
	return Validate(hex, ValidationType::HEX);
}

std::vector<std::pair<std::string, NahidaProject::StringValidator::ValidationResult>> NahidaProject::StringValidator::BatchValidate(const std::vector<std::pair<std::string, ValidationType>>& items) const {
	std::vector<std::pair<std::string, ValidationResult>> results;
	for (const auto& item : items) {
		results.emplace_back(item.first, Validate(item.first, item.second));
	}
	return results;
}
